#pragma once

#include "Interface.h"
#include "../User/Process.h"
#include "../Network/Packet.h"
#include "../User/User.h"
#include "../User/ClientConnection.h"

static int nextTerminalId = 0;

static int getNextTerminalId() 
{
	static Mutex mxLock;
	Lock lock(mxLock);

	nextTerminalId++;
	return nextTerminalId;
}

static int API_CreateOutputWindow(lua_State* vm)
{
	/* only allow singleton access to this function */
	static Mutex mxLock;
	Lock lock(mxLock);

	if(lua_isnumber(vm,1) && lua_isnumber(vm,2) && lua_isnumber(vm,3))
	{

		/*
		*	This API call from the vm will create the terminal output and pass back in the reference for it
		*/

		double displayTerminalType = lua_tonumber(vm, 3);
		double processId = lua_tonumber(vm,2);
		CProcess* p = (CProcess*)(void*)(unsigned int)processId;
		
		unsigned int newTerminalId = getNextTerminalId();

		CPacket* openPacket = new CPacket((BYTE)PACKET_VDOOPEN);
		openPacket->setSubtype((BYTE)displayTerminalType);
		openPacket->setTargetProcess((unsigned int)newTerminalId);

		double userId = lua_tonumber(vm,1);
		CUser* u = (CUser*)(void*)(unsigned int)userId;
		u->addOutboundPacket(openPacket);

		lua_pushnumber(vm, newTerminalId);
		return 1;

	}
	else
	{
		return 0;
	}
	
}

/* console output routines */

static int API_TerminalWriteLine(lua_State* vm)
{
	/* only allow singleton access to this function */
	static Mutex mxLock;
	Lock lock(mxLock);

	if(lua_isnumber(vm,1) && lua_isnumber(vm,2) && lua_isnumber(vm,3) && lua_isstring(vm,4))
	{

		/*
		*	This API call from the vm will pass the output terminal number from within the process
		*/

		string outboundLine = lua_tostring(vm,4);

		double processId = lua_tonumber(vm,2);
		CProcess* p = (CProcess*)(void*)(unsigned int)processId;

		double userId = lua_tonumber(vm,1);
		CUser* u = (CUser*)(void*)(unsigned int)userId;

		double terminalId = lua_tonumber(vm,3);

		CPacket* openPacket = new CPacket((BYTE)PACKET_VDOCHANGES);
		openPacket->setSubtype((BYTE)VCHANGE_LINEOUT);
		openPacket->setTargetProcess((unsigned int)terminalId);
		openPacket->pack(outboundLine);
		u->addOutboundPacket(openPacket);

	}
	else
	{
		/* string value expected for this function */
	}
	return 0;
}

/* filesystem routines */

/* database routines */

/* message interface - outbound from vm */

/* helper functions to maintain stack integrity, ONLY CALL THESE FROM WITHIN THREAD LOCKED ROUTINES */

static void API_SetPropertyValue(lua_State* vm, const char* _property, double _value)
{
	lua_pushnumber(vm, _value);
	lua_setfield(vm,LUA_GLOBALSINDEX, _property);
}

static void API_SetPropertyValue(lua_State* vm, const char* _property, const char* _value)
{
	lua_pushstring(vm, _value);
	lua_setfield(vm,LUA_GLOBALSINDEX, _property);
}

static double API_GetGlobalNumberProperty(lua_State* vm, const char* _property)
{
	
	lua_getfield(vm, LUA_GLOBALSINDEX, _property);
	if(lua_isnumber(vm,-1))
	{
		double retValue = lua_tonumber(vm,-1);
		lua_pop(vm, 1); // pop the value
		lua_pop(vm, 1); // pop the global var
		return retValue;
	}

	return 0;
}

static string API_GetGlobalStringProperty(lua_State* vm, const char* _property)
{
	
	lua_getfield(vm, LUA_GLOBALSINDEX, _property);
	if(lua_isstring(vm,-1))
	{
		string retValue = lua_tostring(vm,-1);
		lua_pop(vm, 1); // pop the value
		lua_pop(vm, 1); // pop the global var
		return retValue;
	}

	return string("");
}

/* registration functions */

void API_InitializeProcess(lua_State* vm, HANDLE userThread, HANDLE processThread)
{
	static Mutex mxLock;
	Lock lock(mxLock);

	luaL_openlibs(vm);

	lua_register(vm, "__APITerminalWriteLine" , API_TerminalWriteLine);
	lua_register(vm, "__APICreateOutputWindow" , API_CreateOutputWindow);

	API_SetPropertyValue(vm,"__userThread", (double)(unsigned int)userThread);
	API_SetPropertyValue(vm,"__processThread", (double)(unsigned int)processThread);
	API_SetPropertyValue(vm, "__terminalMode",(double)0); // initially no console output
	
}